2025-05-20
Moi :
Mon mari :
Et si je faisais une application Shiny ?
De quoi tu as besoin ?
On fait une maquette ?
Informations imbriquées :
N° de ligne et colonne = position physique
Cellule = type de plant + identifiant
Couleurs = méta-informations (plante mangée, limite, etc.)
Étape : Structuration tabulaire
A partir de la table structurée
Dessiner les points pour récupérer des attributs lat, lon
Exporter le fichier .geojson
🔁 Résultat : Un fichier léger, compatible avec de nombreux outils de visualisation web ou R
chenes : coordonnées GPS et métadonnées de chaque chêne
truffe : truffes trouvées au pied d’un chêne
reens : historique des réensemencement (remise en culture)
chenes.idoak est la clé primaire (identifiant du chêne)
truffe.idoak et reens.idoak : clés étrangères vers chenes
✅ Avantages
Simple de prise en main
Open source ou auto-hébergeable
Interface web moderne (SQL, rôles, tables)
API REST auto-générée, interrogeable en R
Logs sur les usages de la BDD
⚠️ Limite
Le besoin : Visualiser et interagir avec les chênes truffier sur une carte cliquable.
La solution JS : Utilisation de Leaflet.js pour afficher des marqueurs pour chaque arbre, avec des interactions qui permettent de récupérer le numéro du chêne cliqué.
Le besoin : Permettre aux utilisateurs d’ajouter des truffes via une interface conviviale, de visualiser les informations concernant un chêne truffier, …
La solution JS : Intégration de SweetAlert2 avec des formulaire HTML dans des modales et y capturer les informations nécessaires.
Le besoin : Afficher des graphiques des rendements de truffes.
La solution JS : Utilisation de Chart.js pour générer des graphiques interactifs basés sur les données de la base.
Le besoin : Disposer d’une application robuste, facilement maintenable et testable
La solution R : Structuration en package {golem} pour séparer clairement interface, logique métier dans des modules.
Le besoin : Utiliser l’application sur smartphone directement au pied des arbres.
La solution R : Utilisation de {shinyMobile} pour une interface responsive, légère.
👉 Un vrai mix R/JS
Peu de fonctions, mais ciblées !
{DBI}✅ Simples, lisibles, maintenables
✅ Métier encapsulé → testable et réutilisable
Une application découpée en briques pour plus de lisibilité
{golem} :
mod_carto_leaflet.R : carte interactivemod_dataviz.R : visualisation des rendementsapp_ui <- function(request) {
tagList(
golem_add_external_resources(),
f7Page(
f7TabLayout(
navbar = f7Navbar(
title = "Les ch\u00eanes truffiers",
hairline = TRUE
),
f7Tabs(
animated = TRUE,
f7Tab(
tabName = "Carte",
icon = f7Icon("map"),
active = TRUE,
f7Card(
title = NULL,
mod_carto_leaflet_ui("carto_leaflet_1")
)
),
f7Tab(
tabName = "Graphe",
icon = f7Icon("graph_square"),
active = FALSE,
f7Card(
title = NULL,
mod_dataviz_ui("dataviz_1")
)
)
)
)
)
)
}Grâce à
golem::invoke_js(), je peux…
# ...
info <- get_info(
dboak = global$chenes,
dbtruffle = global$truffe,
dbreensemence = global$reensemence,
theidoak = input$chene_click
)
golem::invoke_js(
"modal",
list(
id = input$chene_click,
type = info$chene$type,
date_reens = info$reensemence,
date_p = as.Date(info$chene$planting_date),
der_truf = info$truffes$last_truffle,
tot_weight = info$truffes$weight_tot,
last_comment = info$truffes$last_comment,
other_comments = info$truffes$other_comments
)
)
# ...Extrait js:
for (var i = 0; i < locations.length; i++) {
marker = new L.circleMarker([locations[i].lat, locations[i].lon])
.unbindPopup()
.addTo(map)
.on("click", onClick);
marker.id = locations[i].idoak;
if (reens === 0) {
if (locations[i].type === "Normal") {
marker.setStyle({
color: "#FF0000",
fillColor: "#FF0000",
fillOpacity: 1,
});
} else {
marker.setStyle({
color: "#FFA500",
fillColor: "#FFA500",
fillOpacity: 1,
});
}
} else {
if (locations[i].info_reens === "1") {
marker.setStyle({
color: "#00AEEF",
fillColor: "#00AEEF",
fillOpacity: 1,
});
} else {
marker.setStyle({
color: "#7f9199",
fillColor: "#7f9199",
fillOpacity: 1,
});
}
}
}Extrait js:
document.getElementById("identity").addEventListener("click",
() => {
var filledtemplateidentitycard = fillTemplate(
templateidentitycard,
arg
);
Swal.fire({
title: "Carte d identité du chêne",
html: filledtemplateidentitycard,
showCancelButton: false,
confirmButtonText: `Fermer`,
}).then((result) => {
Shiny.setInputValue("chene_click",
null,
{ priority: "event" });
openMainSwal(cheneId);
});
});Exemple template HTML :
<b>Identifiant : </b> {{id}}
<hr> <b>Date de plantation : </b> {{date_p}}
<hr> <b>Type : </b> {{type}}
<hr> <b>Dernier Réensemencement : </b> {{date_reens}}
<hr> <b>Dernière truffe : </b> {{der_truf}}
<hr> <b>Poids total trouvé : </b> {{tot_weight}} g
<hr> <b>Dernier Commentaire : </b> {{last_comment}}
<hr>
<details>
<summary><b>Autres Commentaires :</b></summary>
{{other_comments}}
</details>Extrait js:
Shiny.addCustomMessageHandler('byyear', function(arg) {
if (newChart) newChart.destroy();
const ctx = document.getElementById(arg.id);
newChart = new Chart(ctx, {
type: 'bar',
data: {
labels: arg.labels,
datasets: [{
label: arg.label,
data: arg.data,
borderWidth: 1
}]
},
options: {
aspectRatio: 1.1,
responsive: true,
plugins: {
title: {
display: true,
text: arg.title
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
})📌 Pas de Github Action pour le déploiement de l’application
📥 Déploiement depuis Posit Connect :
récupère le nouveau code depuis Github
reconstruit l’environnement
déploie automatiquement la nouvelle version
{pkgdown}✅ “Ok ça va, j’ai compris comment ça marche, c’est simple !”
🗺️ Visibilité des données
✅ “C’est pratique de voir direct quel arbre a donné quoi”
👆 Points un peu petits sur mobile
🔁 Un switchInput à déplacer pour plus d’ergonomie
💬 Les commentaires :“Finalement, je veux surtout voir le dernier commentaire”🚪 Les modales :
“Ce serait bien de pouvoir passer de la carte d’identité à la déclaration d’une truffe sans être obligé de recliquer sur l’arbre”
Cette application est née d’un besoin concret, sur le terrain et d’un désir d’apprendre.
Elle m’a permis de :
💡 Mettre en application et Approfondir mes compétences en JS.
🤝 Collaborer étroitement avec un utilisateur final… mon mari !
🌱 Créer un outil simple, utile et utilisé.
La vraie vie commence… à la prochaine récolte
Améliorations prévues :
📍 Intégration de la géolocalisation.
🔎 Mieux contrôler/sécuriser les inputs
🔐 Gestion des utilisateurs et des droits d’accès.
🧪 Aller plus loin dans les tests automatisés avec {playwright}.
murielle@thinkr.fr
© Trufficulteurs Beauce-Val de Loire
Truffles : une application Shiny pour cultiver ses données… et ses truffes ! | Retrouvez nous sur https://thinkr.fr